package drds.datasource.connection_pool.impl;

import drds.datasource.connection_pool.api.SqlException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.sql.*;

@Slf4j
public class PooledPreparedStatement extends PooledStatement implements drds.datasource.connection_pool.api.PreparedStatement {


    @Setter
    @Getter
    private final PreparedStatement preparedStatement;

    @Setter
    @Getter
    private int defaultMaxFieldSize;
    @Setter
    @Getter
    private int defaultMaxRows;
    @Setter
    @Getter
    private int defaultQueryTimeout;
    @Setter
    @Getter
    private int defaultFetchDirection;
    @Setter
    @Getter
    private int defaultFetchSize;
    @Setter
    @Getter
    private int currentMaxFieldSize;
    @Setter
    @Getter
    private int currentMaxRows;
    @Setter
    @Getter
    private int currentQueryTimeout;
    @Setter
    @Getter
    private int currentFetchDirection;
    @Setter
    @Getter
    private int currentFetchSize;


    public PooledPreparedStatement(PooledConnection pooledConnection, PreparedStatement preparedStatement)
            throws SqlException {
        super(pooledConnection, preparedStatement);
        this.preparedStatement = preparedStatement;


        currentMaxFieldSize = defaultMaxFieldSize;
        currentMaxRows = defaultMaxRows;
        currentQueryTimeout = defaultQueryTimeout;
        currentFetchDirection = defaultFetchDirection;
        currentFetchSize = defaultFetchSize;
    }


    public void setFetchSize(int rows) throws SqlException {
        currentFetchSize = rows;
        super.setFetchSize(rows);
    }


    public void setQueryTimeout(int seconds) throws SqlException {
        currentQueryTimeout = seconds;
        super.setQueryTimeout(seconds);
    }

    @Override
    public void close() throws SqlException {
        if (isClosed()) {
            return;
        }
        pooledConnection.closePoolableStatement(this);
    }

    void super$close() throws SqlException {
        super.close();
    }

    public void setClosed(boolean value) {
        this.closed = value;
    }

    //
    @Override
    public void setNull(int parameterIndex, int sqlType) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setNull(parameterIndex, sqlType);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setNull()");
        }
    }

    @Override
    public void setString(int parameterIndex, String x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setString(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setString()");
        }
    }

    @Override
    public void setBigDecimal(int parameterIndex, BigDecimal x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setBigDecimal(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setBigDecimal()");
        }
    }


    @Override
    public void setInt(int parameterIndex, int x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setInt(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setInt()");
        }
    }

    @Override
    public void setLong(int parameterIndex, long x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setLong(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setLong()");
        }
    }


    @Override
    public void setDate(int parameterIndex, Date x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setDate(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setDate()");
        }
    }

    @Override
    public void setTime(int parameterIndex, Time x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setTime(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setTime()");
        }
    }

    @Override
    public void setTimestamp(int parameterIndex, Timestamp x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setTimestamp(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setTimestamp()");
        }
    }

    @Override
    public void setObject(int parameterIndex, Object x) throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.setObject(parameterIndex, x);
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.setObject()");
        }
    }

    @Override
    public void clearParameters() throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.clearParameters();
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.clearParameters()");
        }
    }

    @Override
    public ParameterMetaData getParameterMetaData() throws SqlException {
        checkIsOpen();
        try {
            return preparedStatement.getParameterMetaData();
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.getParameterMetaData()");
        }
    }

    //
    public drds.datasource.connection_pool.api.ResultSet executeQuery() throws SqlException {
        checkIsOpen();
        pooledConnection.beforeExecute();
        try {
            ResultSet resultSet = preparedStatement.executeQuery();
            if (resultSet == null) {
                return null;
            }
            PooledResultSet pooledResultSet = new PooledResultSet(this, resultSet);
            addPooledResultSet(pooledResultSet);
            return pooledResultSet;
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.executeQuery()");
        } finally {
            pooledConnection.afterExecute();
        }
    }

    @Override
    public ResultSetMetaData getResultSetMetaData() throws SqlException {
        checkIsOpen();
        try {
            return preparedStatement.getMetaData();
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.getMetaData()");
        }
    }


    public int executeUpdate() throws SqlException {
        checkIsOpen();
        pooledConnection.beforeExecute();
        try {
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.executeUpdate()");
        } finally {
            pooledConnection.afterExecute();
        }
    }
    //

    @Override
    public void addBatch() throws SqlException {
        checkIsOpen();
        try {
            preparedStatement.addBatch();
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.addBatch()");
        }
    }

    public int[] executeBatch() throws SqlException {
        checkIsOpen();
        pooledConnection.beforeExecute();
        try {
            return preparedStatement.executeBatch();
        } catch (SQLException e) {
            throw checkException(e, "preparedStatement.executeBatch()");
        } finally {
            pooledConnection.afterExecute();
        }
    }


}
